package com.example.podcaster.services;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.example.podcaster.services.DownloadTask.State;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

@SuppressLint("UseSparseArrays")
public class DownloadService extends Service implements IDownloadHandler
{
    public class DownloadServiceBinder extends Binder
    {
        public DownloadService getService()
        {
            return DownloadService.this;
        }
    }

    private final static int MAX_CONCURRENT_DOWNLOADS = 2;

    private File queueFile;

    private File downloadTasksFile;

    private final DownloadServiceBinder localBinder = new DownloadServiceBinder();

    private final ThreadPoolExecutor threads = new ThreadPoolExecutor(MAX_CONCURRENT_DOWNLOADS, MAX_CONCURRENT_DOWNLOADS, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    private ArrayList<DownloadTask> queue;

    private HashMap<Integer, DownloadTask> downloadTasks;

    @SuppressWarnings("unchecked")
    @Override
    public void onCreate()
    {
        super.onCreate();

        Context context = this.getApplicationContext();
        File parent = context.getFilesDir();
        
        queueFile = new File(parent, "queue");
        downloadTasksFile = new File(parent, "downloaded_tasks");

        if (queueFile.isFile())
        {
            try
            {
                queue = (ArrayList<DownloadTask>) readObject(queueFile);
            }
            catch (IOException e)
            {
                queue = new ArrayList<DownloadTask>();
            }
        }
        else
        {
            queue = new ArrayList<DownloadTask>();
        }

        if (downloadTasksFile.isFile())
        {
            try
            {
                downloadTasks = (HashMap<Integer, DownloadTask>) readObject(downloadTasksFile);
            }
            catch (IOException e)
            {
                downloadTasks = new HashMap<Integer, DownloadTask>();
            }
        }
        else
        {
            downloadTasks = new HashMap<Integer, DownloadTask>();
        }

        this.startDownloads();
    }

    @Override
    public void onDestroy()
    {
        this.saveDownloads();

        super.onDestroy();
    }

    public synchronized void addDownloadTask(DownloadTask downloadTask)
    {
        downloadTasks.put(downloadTask.getId(), downloadTask);
        queue.add(downloadTask);

        this.saveDownloads();
        this.startDownloads();
    }

    public synchronized DownloadTask getDownloadTask(int id)
    {
        return downloadTasks.get(id);
    }

    public synchronized DownloadTask removeDownloadTask(int id)
    {
        DownloadTask downloadTask = downloadTasks.remove(id);
        queue.remove(downloadTask);
        
        if (!downloadTask.isTerminated())
        {
        	downloadTask.cancel();
        }
        this.saveDownloads();

        return downloadTask;
    }

    public synchronized List<DownloadTask> getDownloadQueue()
    {
        return new ArrayList<DownloadTask>(queue);
    }

    public synchronized List<DownloadTask> getAllDownloadTasks()
    {
        return new ArrayList<DownloadTask>(downloadTasks.values());
    }

    private synchronized void startDownloads()
    {
        for (int i = 0; i < MAX_CONCURRENT_DOWNLOADS && i < queue.size(); ++i)
        {
            DownloadTask downloadTask = queue.get(i);
            if (!downloadTask.isDownloading())
            {
                downloadTask.download();
                threads.execute(new DownloadThread(downloadTask, this));
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return localBinder;
    }

    @Override
    public synchronized void downloadStopped(int id)
    {
        DownloadTask downloadTask = downloadTasks.get(id);
        if (null != downloadTask && downloadTask.isTerminated())
        {
            queue.remove(downloadTask);
        }

        this.saveDownloads();
        this.startDownloads();
    }

    private static Object readObject(File file) throws IOException
    {
        ObjectInputStream objectInputStream = null;
        try
        {
            objectInputStream = new ObjectInputStream(new FileInputStream(file));
            return objectInputStream.readObject();
        }
        catch (IOException e)
        {
            throw e;
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != objectInputStream)
            {
                objectInputStream.close();
            }
        }
        return null;
    }

    private static boolean writeObject(File file, Object o)
    {
        ObjectOutputStream objectOutputStream = null;
        try
        {
            objectOutputStream = new ObjectOutputStream(new FileOutputStream(file, false));
            objectOutputStream.writeObject(o);
            return true;
        }
        catch (IOException e)
        {
            return false;
        }
        finally
        {
            if (null != objectOutputStream)
            {
                try
                {
                    objectOutputStream.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    private boolean saveDownloads()
    {
        return writeObject(queueFile, queue) && writeObject(downloadTasksFile, downloadTasks);
    }
}